/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package fr.ove.openmath.jome.model.processor;

/**
 *
 * @author Mr.Sam
 */
import fr.ove.openmath.jome.model.*;
import fr.ove.openmath.jome.model.events.*;
import java.util.*;
import java.awt.*;

/**
* A processor for the Strahler number.
*
* @author © 2000 DIRAT Laurent
* @version 1.0  16/08/2000
*/
public class StrahlerProcessor extends ProcessorImpl implements StrahlerProcessorInterface {
    /**
    * The list of Strahler numbers in the formula.
    */
    private Vector strahlerNumbers = new Vector();
    
    /**
    * Check if the current list of Strahler numbers in the formula is valid or not.
    */
    private boolean isListValid = false;
        
    /**
    * The constructor.
    */
    public StrahlerProcessor(Formula formula) {
        super(formula);
    }
    
    /**
    * Sets the strahler level form which the processing will be considering.<BR>
    * For convenience only, in most cases this method is equivalent to the
    * @see setLevel of the @see Processor interface.
    * @param strahlerLevel the strahler level.
    */
    public void setStrahlerLevel(int strahlerLevel) {
        setLevel(strahlerLevel);
    }
    
    /**
    * Returns the strahler level form which the processing will be considering.<BR>
    * For convenience only, in most cases this method is equivalent to the
    * @see getLevel of the @see Processor interface.
    */
    public int getStrahlerLevel() {
        return getLevel();
    }
    
    /**
    * Returns the Strahler list of the formula.
    */
    public Vector getStrahlerList() {
        if (!isListValid) {
            buildStrahlerList(getFormula());
            isListValid = true;
        }
        return strahlerNumbers;
    }
    
    /**
    * Returns the biggest Strahler of the formula.
    */
    public int getBiggestStrahler() {
        return ((Integer) strahlerNumbers.lastElement()).intValue();
    }
    
    /**
    * Does processing.
    */
    public void doProcess() {
        Formula formula = getFormula();
        
        if (formula != null) {
            switch (getProcessingId()) {
                case COMPUTE :
                    // Calcul du Strahler de la formule
                    formula.computeNbStrahler();
                    // La liste des Strahler n'est (a priori) plus à jour.
                    strahlerNumbers.removeAllElements();
                    buildStrahlerList(formula);
                    break;
                case SIMPLE_ICONIFICATION :
                    // Iconifie les parties de la formule qui ont un nombre de Strahler inférieur
                    // à celui spécifié.
                    setUpdateDisplay(true);
                    strahleriseTheFTS(formula, getLevel());
                    setUpdateDisplay(false);
                    break;
                case RECURSIVE_ICONIFICATION :
                    // Iconifie les parties de la formule qui ont un nombre de Strahler inférieur
                    // à celui spécifié. Iconification récursive.
                    setUpdateDisplay(true);
                    strahleriseAllTheFTS(formula, getLevel());
                    setUpdateDisplay(false);
                    break;
                case COMPUTE_AND_ICONIFY :
                    // Calcul du Strahler de la formule
                    formula.computeNbStrahler();
                    // La liste des Strahler n'est (a priori) plus à jour.
                    strahlerNumbers.removeAllElements();
                    buildStrahlerList(formula);
                    // Iconifie les parties de la formule qui ont un nombre de Strahler inférieur
                    // à celui spécifié. Iconification récursive.
                    // Le defaut est que l'on icionifie à une valeur correspondant à peu près à la
                    // valeur moyenne du Strahler max calculé.
                    int strahlerValue = getBiggestStrahler() / 2;
                    // Maintenant on regarde dans la liste des Strahlers, le Strahler valide immédiatement
                    // inférieur à la valeur correpsondant à la moitié du plus grand.
                    int count = strahlerNumbers.size();
                    for (int i = 1; i < count; i++ ) {
                        if (((Integer) strahlerNumbers.elementAt(i)).intValue() > strahlerValue) {
                            strahlerValue = ((Integer) strahlerNumbers.elementAt(i-1)).intValue();
                            break;
                        }
                        
                    }
                    setLevel(strahlerValue);
                    strahleriseAllTheFTS(formula, strahlerValue);
            }
        }
    }
    
    /**
    * Iconifies the descendance of the instance which has a Strahler number lower
    * than the specified.
    * @param nbStrahler the specified Strahler number.
    */
    private void strahleriseTheFTS(FormulaTreeStructure fts, int nbStrahler) {
        // On parcourt tous les enfants de l'instance et on descend dans toute l'arboresnce
        // jusqu'au feuilles. (DFS) Arrivée à la feuille, on teste la valeur du strahler et si
        // elle est inférieure à celle spécifiée, on iconifie.
        // On réitère le process sur tous les enfants.
        FormulaTreeStructure child = null;
        if (fts.getNbChildren() > 0) {
            for (Enumeration e = fts.getChildren().elements(); e.hasMoreElements(); ) {
                child = (FormulaTreeStructure) e.nextElement();
                if (child.getNbStrahler() > nbStrahler)
                    strahleriseTheFTS(child, nbStrahler);
                else
                    if (child.getNbStrahler() > 1)
                        strahlerize(child, nbStrahler);
            }
        }
    }
    
    /**
    * Iconifies all the descendance of the instance which has a Strahler number lower
    * than the specified.
    * @param nbStrahler the specified Strahler number.
    */
    private void strahleriseAllTheFTS(FormulaTreeStructure fts, int nbStrahler) {
        // On parcourt tous les enfants de l'instance et on descend dans toute l'arboresnce
        // jusqu'au feuilles. (DFS) Arrivée à la feuille, on teste la valeur du strahler et si
        // elle est inférieure à celle spécifiée, on iconifie.
        // On réitère le process sur tous les enfants.
        if (fts.getNbChildren() > 0) {
            for (Enumeration e = fts.getChildren().elements(); e.hasMoreElements(); ) 
                strahleriseAllTheFTS((FormulaTreeStructure) e.nextElement(), nbStrahler);

            if (fts.getNbStrahler() > 1)
                strahlerize(fts, nbStrahler);
        }
    }
    
    /**
    * Iconifie the instance if it has a Strahler number lower than the specified.
    * @param nbStrahler the specified Strahler number
    */
    private void strahlerize(FormulaTreeStructure fts, int nbStrahler) {
        if ((fts.getNbStrahler() <= nbStrahler) && (fts.getFather() != null)) {
            Icon icon = new Icon(fts);
            // On mets nb strahler et depth de icon avec les valeurs que l'instance a.
            icon.setNbStrahler(fts.getNbStrahler());
            //icon.setDepth(fts.getDepth());
            // On ajoute notre instance à iconifier dans Icon (<=> on iconfie l'instance)
            icon.addIconified(fts);
            // On insère maintenant notre icon à la place de fts.
            // On prend le père de l'instance.
            FormulaTreeStructure father = (FormulaTreeStructure) fts.getFather();
            // On insère notre icone à la place de l'instance
            father.addChild(icon, fts.getRank());
            // On enlève l'instance de la liste des enfants de father.
            father.removeChild(fts);
            
            if (getUpdateDisplay()) {
                // On ajoute un display pour notre icon.
                ModelEvent modelEvent = new ModelEvent(father);
                modelEvent.setAction(ModelEvent.ADD, icon);
                father.fireModelEvent(modelEvent);
            }
        }
    }
    
    /**
    * Builds the list of Strahler values of the formula.
    */
    private void buildStrahlerList(FormulaTreeStructure fts) {
        addNewStrahler(fts.getNbStrahler());
        int count = fts.getNbChildren();
        for (int i = 0; i < count; i++)
            buildStrahlerList((FormulaTreeStructure) fts.getChild(i));
    }
    
    /**
    * Adds the specified Strahler value in the list of Strahler values if necessary
    * @param strahler the Strahler value to add
    */
    private void addNewStrahler(int strahler) {
        int currentStrahler;
        
        //if (strahler != 1) {
            if (strahlerNumbers.size() == 0)
                strahlerNumbers.addElement(new Integer(strahler));
            else {
                for (int i = 0; i < strahlerNumbers.size(); i++) {
                    currentStrahler = ((Integer) strahlerNumbers.elementAt(i)).intValue();
                    if (strahler == currentStrahler)
                        return;
                    else if (strahler < currentStrahler) {
                        strahlerNumbers.insertElementAt(new Integer(strahler), i);
                        return;
                    }
                }
                strahlerNumbers.addElement(new Integer(strahler));
            }
        //}
        
    }
    
    public static void main(String args[]) {
        String exp = "1+2/3*(4+3/5)^2+2/((3+5^9)*(1+2+3))";
        Formula formula = new Formula();
        formula.setDoProcessing(true);
        
        StrahlerProcessor p = new StrahlerProcessor(formula);
        p.setLevel(3);
        formula.setProcessor(p);
        
        fr.ove.openmath.jome.ctrlview.bidim.FormulaDisplay display = new fr.ove.openmath.jome.ctrlview.bidim.FormulaDisplay();
        formula.addModelListener(display);
        display.addControlListener(formula);
        
        java.awt.Frame f = new java.awt.Frame();
        f.setLayout(new java.awt.BorderLayout());
        f.setBounds(50, 50, 250, 80);
        f.add("Center", display);
        f.setVisible(true);
        
        fr.ove.openmath.jome.ctrl.linear.LinearParser linearParser = new fr.ove.openmath.jome.ctrl.linear.LinearParser();
        linearParser.addLinearParserListener(formula);
        linearParser.parse(exp);
        System.out.println("la formule saisie est : \t\t" + exp);
        System.out.println("la formule construite est : \t\t" + formula.getLinear());
    }    
}
